﻿using AutojsController.Config;
using AutojsController.Helpers;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Text.Json;
using System.Threading;
using System;
using System.Text;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using System.Diagnostics;
using Newtonsoft.Json.Linq;

namespace AutojsController.Services
{
    public class MessageEventArgs : EventArgs
    {
        public JObject data; //Json对象

        public MessageEventArgs(string data)
        {
            this.data = JObject.Parse(data);
        }
    }

    public delegate void MessageReceivedEventHandler(MobileConnectionService mcs, MessageEventArgs e);

    /// <summary>
    /// 所有功能参考 https://github.com/SuperMonster003/AutoJs6-VSCode-Extension
    /// </summary>
    public class MobileConnectionService
    {
        public string ip;
        private const int port = 7347;
        private TcpClient tcpClient = new TcpClient();
        private int MessageId = 0;
        private const int ServerHeaderSize = 16;    //往服务端发送头部数据
        private const int ClientHeaderSize = 8;     //从服务端接收头部数据
        public event MessageReceivedEventHandler? MessageReceived;
        private bool _isReading = false;

        public MobileConnectionService(string ip)
        {
            this.ip = ip;
        }

        public MobileConnectionService(Mobile mobile)
        {
            this.ip = mobile.Address;
        }

        public static async Task<MobileConnectionService?> CreateAndConnect(string ip)
        {
            var service = new MobileConnectionService(ip);
            if (await service.Connect())
            {
                return service;
            }
            return null;
        }

        public static async Task<MobileConnectionService?> CreateAndConnect(Mobile mobile)
        {
            var service = new MobileConnectionService(mobile);
            if (await service.Connect())
            {
                return service;
            }
            return null;
        }

        public async Task<bool> Connect()
        {
            try
            {
                if (tcpClient.Connected) return true;
                using var cts = new CancellationTokenSource(Consts.SOCK_CONNECT_TIMEOUT);
                await tcpClient.ConnectAsync(ip, port, cts.Token);
                _isReading = true;
                _ = loopReadAsync();
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 从tcpClient循环读取数据
        /// 根据接收到的数据消息触发MessageReceived事件
        /// </summary>
        private async Task loopReadAsync()
        {
            bool isLastDataComplete = true;
            MemoryStream msgStream = new MemoryStream();
            int dataLength = 0;
            int dataType = 1;   //ts里取了根本没使用
            int expectedLength = 0;
            while (_isReading)
            {
                if (!tcpClient.Connected) break;
                try
                {
                    using var stream = tcpClient.GetStream();
                    var tmpMemStream = new MemoryStream();
                    await stream.CopyToAsync(tmpMemStream);
                    var readBytes = tmpMemStream.ToArray();
                    var offset = 0;
                    if (isLastDataComplete)
                    {
                        msgStream.SetLength(0);
                        msgStream.Position = 0;
                        dataLength = BitConverter.ToInt32(readBytes, 0);
                        dataType = BitConverter.ToInt16(readBytes, 4);
                        expectedLength = ClientHeaderSize + dataLength;
                        offset += ClientHeaderSize;
                    }
                    await msgStream.WriteAsync(readBytes.Skip(offset).ToArray(), (int)msgStream.Length, readBytes.Length - offset);
                    if (msgStream.Length >= expectedLength)
                    {
                        isLastDataComplete = true;
                        MessageReceived?.Invoke(this, new MessageEventArgs(msgStream.ToString()??""));
                    }
                    else
                    {
                        isLastDataComplete = false;
                    }
                }
                catch(Exception ex) //TODO:具体异常类型具体分析
                {
                    Debug.WriteLine($"读取退出:{ex.ToString()}");
                    _isReading = false;
                    break;
                }
            }
        }

        /// <summary>
        /// 往autojs手机端发送数据
        /// 先发送数据长度，再发送数据本体
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        /// <exception cref="MobileConnectException"></exception>
        private async Task Send(object data, SocketSendMessageType type)
        {
            byte[] message;
            switch (type)
            {
                case SocketSendMessageType.Json:
                    var json = JsonConvert.SerializeObject(data);
                    message = Encoding.UTF8.GetBytes(json);
                    break;
                case SocketSendMessageType.Bytes:
                    if (data is byte[] bytes)
                    {
                        message = bytes;
                    }
                    else
                    {
                        message = Encoding.UTF8.GetBytes(data.ToString() ?? "");
                    }
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(type), type, null);
            }
            using var stream = tcpClient.GetStream();
            using var cts = new CancellationTokenSource(Consts.SOCK_CONNECT_TIMEOUT);
            var header = new byte[ServerHeaderSize];
            var lengthBytes = BitConverter.GetBytes(message.Length);
            Buffer.BlockCopy(lengthBytes, 0, header, 0, lengthBytes.Length);
            var msgTypeBytes = BitConverter.GetBytes((int)type);
            Buffer.BlockCopy(msgTypeBytes, 0, header, ServerHeaderSize - 2, 2);
            try
            {
                await stream.WriteAsync(header, cts.Token);
                await stream.WriteAsync(message.AsMemory(0, message.Length), cts.Token);
            }
            catch
            {
                throw new MobileConnectException($"{ip}连接已断开");
            }
        }

        // 根据TS代码，发送hello可以拿到设备信息，目前没有实现
        public async Task sendHello()
        {
            MessageId += 1;
            try
            {
                await Connect();
                await Send(new { id = MessageId, type = "hello", data = new { } }, SocketSendMessageType.Json);
            }
            catch (Exception ex)
            {
                throw new MobileConnectException("无法连接");
            }
        }

        public void Disconnect()
        {
            try
            {
                if (tcpClient.Connected)
                {
                    tcpClient.Close();
                }
                _isReading = false;
            }
            catch
            {
                //ignore
            }
        }
    }
}
